﻿// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information
// 
// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to you under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License. You may obtain a copy of the License at
// 
// http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// 

using System;
using System.IO;
using System.Collections;

using log4net.Filter;
using log4net.Util;
using log4net.Layout;
using log4net.Core;

namespace log4net.Appender
{
    /// <summary>
    /// Abstract base class implementation of <see cref="IAppender"/>. 
    /// </summary>
    /// <remarks>
    /// <para>
    /// This class provides the code for common functionality, such 
    /// as support for threshold filtering and support for general filters.
    /// </para>
    /// <para>
    /// Appenders can also implement the <see cref="IOptionHandler"/> interface. Therefore
    /// they would require that the <see cref="M:IOptionHandler.ActivateOptions()"/> method
    /// be called after the appenders properties have been configured.
    /// </para>
    /// </remarks>
    /// <author>Nicko Cadell</author>
    /// <author>Gert Driesen</author>
    public abstract class AppenderSkeleton : IAppender, IBulkAppender, IOptionHandler, IFlushable
    {
        /// <summary>
        /// Default constructor
        /// </summary>
        /// <remarks>
        /// <para>Empty default constructor</para>
        /// </remarks>
        protected AppenderSkeleton()
        {
            this.m_errorHandler = new OnlyOnceErrorHandler(this.GetType().Name);
        }

        /// <summary>
        /// Finalizes this appender by calling the implementation's 
        /// <see cref="Close"/> method.
        /// </summary>
        /// <remarks>
        /// <para>
        /// If this appender has not been closed then the <c>Finalize</c> method
        /// will call <see cref="Close"/>.
        /// </para>
        /// </remarks>
        ~AppenderSkeleton() 
        {
            // An appender might be closed then garbage collected. 
            // There is no point in closing twice.
            if (!this.m_closed) 
            {
                LogLog.Debug(declaringType, "Finalizing appender named ["+ this.m_name+"].");
                this.Close();
            }
        }

        /// <summary>
        /// Gets or sets the threshold <see cref="Level"/> of this appender.
        /// </summary>
        /// <value>
        /// The threshold <see cref="Level"/> of the appender. 
        /// </value>
        /// <remarks>
        /// <para>
        /// All log events with lower level than the threshold level are ignored 
        /// by the appender.
        /// </para>
        /// <para>
        /// In configuration files this option is specified by setting the
        /// value of the <see cref="Threshold"/> option to a level
        /// string, such as "DEBUG", "INFO" and so on.
        /// </para>
        /// </remarks>
        public Level Threshold 
        {
            get { return this.m_threshold; }
            set { this.m_threshold = value; }
        }

        /// <summary>
        /// Gets or sets the <see cref="IErrorHandler"/> for this appender.
        /// </summary>
        /// <value>The <see cref="IErrorHandler"/> of the appender</value>
        /// <remarks>
        /// <para>
        /// The <see cref="AppenderSkeleton"/> provides a default 
        /// implementation for the <see cref="ErrorHandler"/> property. 
        /// </para>
        /// </remarks>
        public virtual IErrorHandler ErrorHandler 
        {
            get { return this.m_errorHandler; }
            set 
            {
                lock(this) 
                {
                    if (value == null) 
                    {
                        // We do not throw exception here since the cause is probably a
                        // bad config file.
                        LogLog.Warn(declaringType, "You have tried to set a null error-handler.");
                    } 
                    else 
                    {
                        this.m_errorHandler = value;
                    }
                }
            }
        }

        /// <summary>
        /// The filter chain.
        /// </summary>
        /// <value>The head of the filter chain filter chain.</value>
        /// <remarks>
        /// <para>
        /// Returns the head Filter. The Filters are organized in a linked list
        /// and so all Filters on this Appender are available through the result.
        /// </para>
        /// </remarks>
        public virtual IFilter FilterHead
        {
            get { return this.m_headFilter; }
        }

        /// <summary>
        /// Gets or sets the <see cref="ILayout"/> for this appender.
        /// </summary>
        /// <value>The layout of the appender.</value>
        /// <remarks>
        /// <para>
        /// See <see cref="RequiresLayout"/> for more information.
        /// </para>
        /// </remarks>
        /// <seealso cref="RequiresLayout"/>
        public virtual ILayout Layout 
        {
            get { return this.m_layout; }
            set { this.m_layout = value; }
        }

        /// <summary>
        /// Initialize the appender based on the options set
        /// </summary>
        /// <remarks>
        /// <para>
        /// This is part of the <see cref="IOptionHandler"/> delayed object
        /// activation scheme. The <see cref="ActivateOptions"/> method must 
        /// be called on this object after the configuration properties have
        /// been set. Until <see cref="ActivateOptions"/> is called this
        /// object is in an undefined state and must not be used. 
        /// </para>
        /// <para>
        /// If any of the configuration properties are modified then 
        /// <see cref="ActivateOptions"/> must be called again.
        /// </para>
        /// </remarks>
        public virtual void ActivateOptions() 
        {
        }

        /// <summary>
        /// Gets or sets the name of this appender.
        /// </summary>
        /// <value>The name of the appender.</value>
        /// <remarks>
        /// <para>
        /// The name uniquely identifies the appender.
        /// </para>
        /// </remarks>
        public string Name 
        {
            get { return this.m_name; }
            set { this.m_name = value; }
        }

        /// <summary>
        /// Closes the appender and release resources.
        /// </summary>
        /// <remarks>
        /// <para>
        /// Release any resources allocated within the appender such as file handles, 
        /// network connections, etc.
        /// </para>
        /// <para>
        /// It is a programming error to append to a closed appender.
        /// </para>
        /// <para>
        /// This method cannot be overridden by subclasses. This method 
        /// delegates the closing of the appender to the <see cref="OnClose"/>
        /// method which must be overridden in the subclass.
        /// </para>
        /// </remarks>
        public void Close()
        {
            // This lock prevents the appender being closed while it is still appending
            lock(this)
            {
                if (!this.m_closed)
                {
                    this.OnClose();
                    this.m_closed = true;
                }
            }
        }

        /// <summary>
        /// Performs threshold checks and invokes filters before 
        /// delegating actual logging to the subclasses specific 
        /// <see cref="M:Append(LoggingEvent)"/> method.
        /// </summary>
        /// <param name="loggingEvent">The event to log.</param>
        /// <remarks>
        /// <para>
        /// This method cannot be overridden by derived classes. A
        /// derived class should override the <see cref="M:Append(LoggingEvent)"/> method
        /// which is called by this method.
        /// </para>
        /// <para>
        /// The implementation of this method is as follows:
        /// </para>
        /// <para>
        /// <list type="bullet">
        ///         <item>
        ///             <description>
        ///             Checks that the severity of the <paramref name="loggingEvent"/>
        ///             is greater than or equal to the <see cref="Threshold"/> of this
        ///             appender.</description>
        ///         </item>
        ///         <item>
        ///             <description>
        ///             Checks that the <see cref="IFilter"/> chain accepts the 
        ///             <paramref name="loggingEvent"/>.
        ///             </description>
        ///         </item>
        ///         <item>
        ///             <description>
        ///             Calls <see cref="M:PreAppendCheck()"/> and checks that 
        ///             it returns <c>true</c>.</description>
        ///         </item>
        /// </list>
        /// </para>
        /// <para>
        /// If all of the above steps succeed then the <paramref name="loggingEvent"/>
        /// will be passed to the abstract <see cref="M:Append(LoggingEvent)"/> method.
        /// </para>
        /// </remarks>
        public void DoAppend(LoggingEvent loggingEvent) 
        {
            // This lock is absolutely critical for correct formatting
            // of the message in a multi-threaded environment.  Without
            // this, the message may be broken up into elements from
            // multiple thread contexts (like get the wrong thread ID).

            lock(this)
            {
                if (this.m_closed)
                {
                    this.ErrorHandler.Error("Attempted to append to closed appender named ["+ this.m_name+"].");
                    return;
                }

                // prevent re-entry
                if (this.m_recursiveGuard)
                {
                    return;
                }

                try
                {
                    this.m_recursiveGuard = true;

                    if (this.FilterEvent(loggingEvent) && this.PreAppendCheck())
                    {
                        this.Append(loggingEvent);
                    }
                }
                catch(Exception ex)
                {
                    this.ErrorHandler.Error("Failed in DoAppend", ex);
                }
#if !MONO && !NET_2_0 && !NETSTANDARD
                // on .NET 2.0 (and higher) and Mono (all profiles), 
                // exceptions that do not derive from System.Exception will be
                // wrapped in a RuntimeWrappedException by the runtime, and as
                // such will be catched by the catch clause above
                catch
                {
                    // Catch handler for non System.Exception types
                    ErrorHandler.Error("Failed in DoAppend (unknown exception)");
                }
#endif
                finally
                {
                    this.m_recursiveGuard = false;
                }
            }
        }

        /// <summary>
        /// Performs threshold checks and invokes filters before 
        /// delegating actual logging to the subclasses specific 
        /// <see cref="M:Append(LoggingEvent[])"/> method.
        /// </summary>
        /// <param name="loggingEvents">The array of events to log.</param>
        /// <remarks>
        /// <para>
        /// This method cannot be overridden by derived classes. A
        /// derived class should override the <see cref="M:Append(LoggingEvent[])"/> method
        /// which is called by this method.
        /// </para>
        /// <para>
        /// The implementation of this method is as follows:
        /// </para>
        /// <para>
        /// <list type="bullet">
        ///         <item>
        ///             <description>
        ///             Checks that the severity of the <paramref name="loggingEvents"/>
        ///             is greater than or equal to the <see cref="Threshold"/> of this
        ///             appender.</description>
        ///         </item>
        ///         <item>
        ///             <description>
        ///             Checks that the <see cref="IFilter"/> chain accepts the 
        ///             <paramref name="loggingEvents"/>.
        ///             </description>
        ///         </item>
        ///         <item>
        ///             <description>
        ///             Calls <see cref="M:PreAppendCheck()"/> and checks that 
        ///             it returns <c>true</c>.</description>
        ///         </item>
        /// </list>
        /// </para>
        /// <para>
        /// If all of the above steps succeed then the <paramref name="loggingEvents"/>
        /// will be passed to the <see cref="M:Append(LoggingEvent[])"/> method.
        /// </para>
        /// </remarks>
        public void DoAppend(LoggingEvent[] loggingEvents) 
        {
            // This lock is absolutely critical for correct formatting
            // of the message in a multi-threaded environment.  Without
            // this, the message may be broken up into elements from
            // multiple thread contexts (like get the wrong thread ID).

            lock(this)
            {
                if (this.m_closed)
                {
                    this.ErrorHandler.Error("Attempted to append to closed appender named ["+ this.m_name+"].");
                    return;
                }

                // prevent re-entry
                if (this.m_recursiveGuard)
                {
                    return;
                }

                try
                {
                    this.m_recursiveGuard = true;

                    ArrayList filteredEvents = new ArrayList(loggingEvents.Length);

                    foreach(LoggingEvent loggingEvent in loggingEvents)
                    {
                        if (this.FilterEvent(loggingEvent))
                        {
                            filteredEvents.Add(loggingEvent);
                        }
                    }

                    if (filteredEvents.Count > 0 && this.PreAppendCheck())
                    {
                        this.Append((LoggingEvent[])filteredEvents.ToArray(typeof(LoggingEvent)));
                    }
                }
                catch(Exception ex)
                {
                    this.ErrorHandler.Error("Failed in Bulk DoAppend", ex);
                }
#if !MONO && !NET_2_0 && !NETSTANDARD
                // on .NET 2.0 (and higher) and Mono (all profiles), 
                // exceptions that do not derive from System.Exception will be
                // wrapped in a RuntimeWrappedException by the runtime, and as
                // such will be catched by the catch clause above
                catch
                {
                    // Catch handler for non System.Exception types
                    ErrorHandler.Error("Failed in Bulk DoAppend (unknown exception)");
                }
#endif
                finally
                {
                    this.m_recursiveGuard = false;
                }
            }
        }

        /// <summary>
        /// Test if the logging event should we output by this appender
        /// </summary>
        /// <param name="loggingEvent">the event to test</param>
        /// <returns><c>true</c> if the event should be output, <c>false</c> if the event should be ignored</returns>
        /// <remarks>
        /// <para>
        /// This method checks the logging event against the threshold level set
        /// on this appender and also against the filters specified on this
        /// appender.
        /// </para>
        /// <para>
        /// The implementation of this method is as follows:
        /// </para>
        /// <para>
        /// <list type="bullet">
        ///        <item>
        ///            <description>
        ///            Checks that the severity of the <paramref name="loggingEvent"/>
        ///            is greater than or equal to the <see cref="Threshold"/> of this
        ///            appender.</description>
        ///        </item>
        ///        <item>
        ///            <description>
        ///            Checks that the <see cref="IFilter"/> chain accepts the 
        ///            <paramref name="loggingEvent"/>.
        ///            </description>
        ///        </item>
        /// </list>
        /// </para>
        /// </remarks>
        protected virtual bool FilterEvent(LoggingEvent loggingEvent)
        {
            if (!this.IsAsSevereAsThreshold(loggingEvent.Level)) 
            {
                return false;
            }

            IFilter f = this.FilterHead;

            while(f != null) 
            {
                switch(f.Decide(loggingEvent)) 
                {
                    case FilterDecision.Deny: 
                        return false;	// Return without appending

                    case FilterDecision.Accept:
                        f = null;		// Break out of the loop
                        break;

                    case FilterDecision.Neutral:
                        f = f.Next;		// Move to next filter
                        break;
                }
            }

            return true;
        }

        /// <summary>
        /// Adds a filter to the end of the filter chain.
        /// </summary>
        /// <param name="filter">the filter to add to this appender</param>
        /// <remarks>
        /// <para>
        /// The Filters are organized in a linked list.
        /// </para>
        /// <para>
        /// Setting this property causes the new filter to be pushed onto the 
        /// back of the filter chain.
        /// </para>
        /// </remarks>
        public virtual void AddFilter(IFilter filter)
        {
            if (filter == null)
            {
                throw new ArgumentNullException("filter param must not be null");
            }

            if (this.m_headFilter == null) 
            {
                this.m_headFilter = this.m_tailFilter = filter;
            } 
            else 
            {
                this.m_tailFilter.Next = filter;
                this.m_tailFilter = filter;	
            }
        }

        /// <summary>
        /// Clears the filter list for this appender.
        /// </summary>
        /// <remarks>
        /// <para>
        /// Clears the filter list for this appender.
        /// </para>
        /// </remarks>
        public virtual void ClearFilters()
        {
            this.m_headFilter = this.m_tailFilter = null;
        }

        /// <summary>
        /// Checks if the message level is below this appender's threshold.
        /// </summary>
        /// <param name="level"><see cref="Level"/> to test against.</param>
        /// <remarks>
        /// <para>
        /// If there is no threshold set, then the return value is always <c>true</c>.
        /// </para>
        /// </remarks>
        /// <returns>
        /// <c>true</c> if the <paramref name="level"/> meets the <see cref="Threshold"/> 
        /// requirements of this appender.
        /// </returns>
        protected virtual bool IsAsSevereAsThreshold(Level level) 
        {
            return ((this.m_threshold == null) || level >= this.m_threshold);
        }

        /// <summary>
        /// Is called when the appender is closed. Derived classes should override 
        /// this method if resources need to be released.
        /// </summary>
        /// <remarks>
        /// <para>
        /// Releases any resources allocated within the appender such as file handles, 
        /// network connections, etc.
        /// </para>
        /// <para>
        /// It is a programming error to append to a closed appender.
        /// </para>
        /// </remarks>
        protected virtual void OnClose() 
        {
            // Do nothing by default
        }

        /// <summary>
        /// Subclasses of <see cref="AppenderSkeleton"/> should implement this method 
        /// to perform actual logging.
        /// </summary>
        /// <param name="loggingEvent">The event to append.</param>
        /// <remarks>
        /// <para>
        /// A subclass must implement this method to perform
        /// logging of the <paramref name="loggingEvent"/>.
        /// </para>
        /// <para>This method will be called by <see cref="M:DoAppend(LoggingEvent)"/>
        /// if all the conditions listed for that method are met.
        /// </para>
        /// <para>
        /// To restrict the logging of events in the appender
        /// override the <see cref="M:PreAppendCheck()"/> method.
        /// </para>
        /// </remarks>
        protected abstract void Append(LoggingEvent loggingEvent);

        /// <summary>
        /// Append a bulk array of logging events.
        /// </summary>
        /// <param name="loggingEvents">the array of logging events</param>
        /// <remarks>
        /// <para>
        /// This base class implementation calls the <see cref="M:Append(LoggingEvent)"/>
        /// method for each element in the bulk array.
        /// </para>
        /// <para>
        /// A sub class that can better process a bulk array of events should
        /// override this method in addition to <see cref="M:Append(LoggingEvent)"/>.
        /// </para>
        /// </remarks>
        protected virtual void Append(LoggingEvent[] loggingEvents)
        {
            foreach(LoggingEvent loggingEvent in loggingEvents)
            {
                this.Append(loggingEvent);
            }
        }

        /// <summary>
        /// Called before <see cref="M:Append(LoggingEvent)"/> as a precondition.
        /// </summary>
        /// <remarks>
        /// <para>
        /// This method is called by <see cref="M:DoAppend(LoggingEvent)"/>
        /// before the call to the abstract <see cref="M:Append(LoggingEvent)"/> method.
        /// </para>
        /// <para>
        /// This method can be overridden in a subclass to extend the checks 
        /// made before the event is passed to the <see cref="M:Append(LoggingEvent)"/> method.
        /// </para>
        /// <para>
        /// A subclass should ensure that they delegate this call to
        /// this base class if it is overridden.
        /// </para>
        /// </remarks>
        /// <returns><c>true</c> if the call to <see cref="M:Append(LoggingEvent)"/> should proceed.</returns>
        protected virtual bool PreAppendCheck()
        {
            if ((this.m_layout == null) && this.RequiresLayout)
            {
                this.ErrorHandler.Error("AppenderSkeleton: No layout set for the appender named ["+ this.m_name+"].");
                return false;
            }

            return true;
        }

        /// <summary>
        /// Renders the <see cref="LoggingEvent"/> to a string.
        /// </summary>
        /// <param name="loggingEvent">The event to render.</param>
        /// <returns>The event rendered as a string.</returns>
        /// <remarks>
        /// <para>
        /// Helper method to render a <see cref="LoggingEvent"/> to 
        /// a string. This appender must have a <see cref="Layout"/>
        /// set to render the <paramref name="loggingEvent"/> to 
        /// a string.
        /// </para>
        /// <para>If there is exception data in the logging event and 
        /// the layout does not process the exception, this method 
        /// will append the exception text to the rendered string.
        /// </para>
        /// <para>
        /// Where possible use the alternative version of this method
        /// <see cref="M:RenderLoggingEvent(TextWriter,LoggingEvent)"/>.
        /// That method streams the rendering onto an existing Writer
        /// which can give better performance if the caller already has
        /// a <see cref="TextWriter"/> open and ready for writing.
        /// </para>
        /// </remarks>
        protected string RenderLoggingEvent(LoggingEvent loggingEvent)
        {
            // Create the render writer on first use
            if (this.m_renderWriter == null)
            {
                this.m_renderWriter = new ReusableStringWriter(System.Globalization.CultureInfo.InvariantCulture);
            }

            lock (this.m_renderWriter)
            {
                // Reset the writer so we can reuse it
                this.m_renderWriter.Reset(c_renderBufferMaxCapacity, c_renderBufferSize);

                this.RenderLoggingEvent(this.m_renderWriter, loggingEvent);
                return this.m_renderWriter.ToString();
            }
        }

        /// <summary>
        /// Renders the <see cref="LoggingEvent"/> to a string.
        /// </summary>
        /// <param name="loggingEvent">The event to render.</param>
        /// <param name="writer">The TextWriter to write the formatted event to</param>
        /// <remarks>
        /// <para>
        /// Helper method to render a <see cref="LoggingEvent"/> to 
        /// a string. This appender must have a <see cref="Layout"/>
        /// set to render the <paramref name="loggingEvent"/> to 
        /// a string.
        /// </para>
        /// <para>If there is exception data in the logging event and 
        /// the layout does not process the exception, this method 
        /// will append the exception text to the rendered string.
        /// </para>
        /// <para>
        /// Use this method in preference to <see cref="M:RenderLoggingEvent(LoggingEvent)"/>
        /// where possible. If, however, the caller needs to render the event
        /// to a string then <see cref="M:RenderLoggingEvent(LoggingEvent)"/> does
        /// provide an efficient mechanism for doing so.
        /// </para>
        /// </remarks>
        protected void RenderLoggingEvent(TextWriter writer, LoggingEvent loggingEvent)
        {
            if (this.m_layout == null) 
            {
                throw new InvalidOperationException("A layout must be set");
            }

            if (this.m_layout.IgnoresException) 
            {
                string exceptionStr = loggingEvent.GetExceptionString();
                if (exceptionStr != null && exceptionStr.Length > 0) 
                {
                    // render the event and the exception
                    this.m_layout.Format(writer, loggingEvent);
                    writer.WriteLine(exceptionStr);
                }
                else 
                {
                    // there is no exception to render
                    this.m_layout.Format(writer, loggingEvent);
                }
            }
            else 
            {
                // The layout will render the exception
                this.m_layout.Format(writer, loggingEvent);
            }
        }

        /// <summary>
        /// Tests if this appender requires a <see cref="Layout"/> to be set.
        /// </summary>
        /// <remarks>
        /// <para>
        /// In the rather exceptional case, where the appender 
        /// implementation admits a layout but can also work without it, 
        /// then the appender should return <c>true</c>.
        /// </para>
        /// <para>
        /// This default implementation always returns <c>false</c>.
        /// </para>
        /// </remarks>
        /// <returns>
        /// <c>true</c> if the appender requires a layout object, otherwise <c>false</c>.
        /// </returns>
        protected virtual bool RequiresLayout
        {
            get { return false; }
        }

        /// <summary>
            /// Flushes any buffered log data.
            /// </summary>
        /// <remarks>
        /// This implementation doesn't flush anything and always returns true
        /// </remarks>
            /// <returns><c>True</c> if all logging events were flushed successfully, else <c>false</c>.</returns>
            public virtual bool Flush(int millisecondsTimeout)
            {
            return true;
            }

        /// <summary>
        /// The layout of this appender.
        /// </summary>
        /// <remarks>
        /// See <see cref="Layout"/> for more information.
        /// </remarks>
        private ILayout m_layout;

        /// <summary>
        /// The name of this appender.
        /// </summary>
        /// <remarks>
        /// See <see cref="Name"/> for more information.
        /// </remarks>
        private string m_name;

        /// <summary>
        /// The level threshold of this appender.
        /// </summary>
        /// <remarks>
        /// <para>
        /// There is no level threshold filtering by default.
        /// </para>
        /// <para>
        /// See <see cref="Threshold"/> for more information.
        /// </para>
        /// </remarks>
        private Level m_threshold;

        /// <summary>
        /// It is assumed and enforced that errorHandler is never null.
        /// </summary>
        /// <remarks>
        /// <para>
        /// It is assumed and enforced that errorHandler is never null.
        /// </para>
        /// <para>
        /// See <see cref="ErrorHandler"/> for more information.
        /// </para>
        /// </remarks>
        private IErrorHandler m_errorHandler;

        /// <summary>
        /// The first filter in the filter chain.
        /// </summary>
        /// <remarks>
        /// <para>
        /// Set to <c>null</c> initially.
        /// </para>
        /// <para>
        /// See <see cref="IFilter"/> for more information.
        /// </para>
        /// </remarks>
        private IFilter m_headFilter;

        /// <summary>
        /// The last filter in the filter chain.
        /// </summary>
        /// <remarks>
        /// See <see cref="IFilter"/> for more information.
        /// </remarks>
        private IFilter m_tailFilter;

        /// <summary>
        /// Flag indicating if this appender is closed.
        /// </summary>
        /// <remarks>
        /// See <see cref="Close"/> for more information.
        /// </remarks>
        private bool m_closed = false;

        /// <summary>
        /// The guard prevents an appender from repeatedly calling its own DoAppend method
        /// </summary>
        private bool m_recursiveGuard = false;

        /// <summary>
        /// StringWriter used to render events
        /// </summary>
        private ReusableStringWriter m_renderWriter = null;

        /// <summary>
        /// Initial buffer size
        /// </summary>
        private const int c_renderBufferSize = 256;

        /// <summary>
        /// Maximum buffer size before it is recycled
        /// </summary>
        private const int c_renderBufferMaxCapacity = 1024;

        /// <summary>
        /// The fully qualified type of the AppenderSkeleton class.
        /// </summary>
        /// <remarks>
        /// Used by the internal logger to record the Type of the
        /// log message.
        /// </remarks>
        private static readonly Type declaringType = typeof(AppenderSkeleton);
    }
}
